package cn.com.miao.lynx.common.log.aspect;

import cn.com.miao.lynx.common.core.util.SpringContextUtil;
import cn.com.miao.lynx.common.core.util.UserAgentUtil;
import cn.com.miao.lynx.common.log.annotation.Log;
import cn.com.miao.lynx.common.log.api.LogDetail;
import cn.com.miao.lynx.common.log.envnt.LogEvent;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.http.HttpUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.Objects;

/**
 * LogAspect
 * 日志切面
 * @title: LogAspect
 * @description:
 * @author: dengmiao
 * @create: 2019-12-27 10:17
 **/
@Slf4j
@Order(value = 0)
@Aspect
public class LogAspect {

    @Pointcut("execution(* *.com.miao..*.api.*.*(..)) || execution(* *.com.miao..*.base.*Controller.*(..)) )")
    public void log() {}

    // @Before("log()")
    public void logBefore(JoinPoint joinPoint) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        assert attributes != null;
        HttpServletRequest request = attributes.getRequest();
        if (log.isInfoEnabled()) {
            log.info("----------------------------------------------------------");
            log.info("用户代理:{}", UserAgentUtil.getUserAgent(request));
            log.info("请求路径:{}", request.getRequestURL().toString());
            log.info("请求类型:{}", request.getMethod());
            log.info("客户端IP:{}", request.getRemoteAddr());
            log.info("请求方法:{}", joinPoint.getSignature().getDeclaringTypeName() + StrUtil.DOT + joinPoint.getSignature().getName());
            log.info("请求参数:{}", Arrays.toString(joinPoint.getArgs()));
        }
    }

    // @AfterReturning(pointcut = "log()", returning = "returnValue")
    public void logAfterReturning(Object returnValue) {
        if (StringUtils.isEmpty(returnValue)) {
            returnValue = "";
        }
        if (log.isInfoEnabled()) {
            log.info("请求响应:{}", returnValue);
            log.info("----------------------------------------------------------");
        }
    }

    @Around("@annotation(logger)")
    public Object around(ProceedingJoinPoint point, Log logger) {
        String strClassName = point.getTarget().getClass().getName();
        String strMethodName = point.getSignature().getName();
        String methodName = StrUtil.builder(strClassName, StrUtil.DOT, strMethodName).toString();
        logBefore(point);

        Instant start = Instant.now();
        Object result = null;
        String exception = null;
        try {
            result = point.proceed();
        } catch (Throwable throwable) {
            exception = throwable.getMessage();
            throwable.printStackTrace();
        }
        logAfterReturning(result);
        if (logger.persistence()) {
            long between = ChronoUnit.MILLIS.between(start, Instant.now());
            LogDetail logDetail = getLogDetail();
            logDetail.setMethodName(methodName);
            logDetail.setException(exception);
            logDetail.setTitle(logger.value());
            logDetail.setType(logger.type());
            logDetail.setTime(between);
            // 日志异步入库
            SpringContextUtil.publishEvent(new LogEvent(logDetail));
        }
        return result;
    }

    private LogDetail getLogDetail() {
        HttpServletRequest request = ((ServletRequestAttributes) Objects
                .requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        return LogDetail.builder()
                .remoteAddr(ServletUtil.getClientIP(request))
                .requestUri(URLUtil.getPath(request.getRequestURI()))
                .method(request.getMethod())
                .userAgent(request.getHeader("user-agent"))
                .params(HttpUtil.toParams(request.getParameterMap()))
                .build();
    }
}
